<template>
  <!-- 角色列表组件 -->
  <div class="roles_cont">
    <!-- 面包屑导航栏区域 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>权限管理</el-breadcrumb-item>
      <el-breadcrumb-item>角色列表</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 卡片展示权限列表内容区域 -->
    <el-card class="box-card">
      <!-- 添加角色按钮 -->
      <el-row>
        <el-button type="primary" @click="addRolesListBut">添加角色</el-button>
      </el-row>
      <!-- 角色列表表格区域 -->
      <el-table :data="rolesList2" style="width: 100%" border>
        <!-- 表格最开始 > 按钮展开列区域 -->
        <el-table-column type="expand" width="80">
          <template slot-scope="scope">
            <el-row v-for="(item1) in scope.row.children" :key="item1.id" :class="['row_bottom','row_display']">
              <!-- 一级权限区域 -->
              <el-col :span="4">
                <el-tag closable @close="removeRightById(scope.row,item1.id)">{{item1.authName}}</el-tag>
                <i class="el-icon-caret-right"></i>
              </el-col>
              <!-- 二级权限区域 -->
              <el-col :span="20">
                <el-row v-for="(item2,index2) in item1.children" :key="item2.id" :class="[index2===0?'':'row_top']">
                  <el-col :span="5">
                    <el-tag type="success" closable @close="removeRightById(scope.row,item2.id)">{{item2.authName}}
                    </el-tag>
                    <i class="el-icon-caret-right"></i>
                  </el-col>
                  <el-col :span="19" :class="['row_display1']">
                    <el-row v-for="(item3) in item2.children" :key="item3.id" :class="['row_display2']">
                      <el-tag type="warning" closable @close="removeRightById(scope.row,item3.id)">{{item3.authName}}
                      </el-tag>
                    </el-row>
                  </el-col>
                </el-row>
              </el-col>
            </el-row>
          </template>
        </el-table-column>
        <!-- 角色权限展示区域 -->
        <el-table-column type="index" width="80"></el-table-column>
        <el-table-column prop="roleName" label="角色名称"></el-table-column>
        <el-table-column prop="roleDesc" label="角色描述"></el-table-column>
        <el-table-column prop="" label="操作" width="320">
          <template slot-scope="scope">
            <el-button type="primary" size="mini" icon="el-icon-edit" @click="alterRolesBut(scope.row)">编辑</el-button>
            <el-button type="danger" size="mini" icon="el-icon-delete" @click="removeRolesBut(scope.row.id)">删除
            </el-button>
            <el-button type="warning" size="mini" icon="el-icon-setting" @click="setRolesBut(scope.row)">分配权限
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-------------------------------------- 重点：优化（数据全部获取到了） ---------------------------------------->
      <!-- 分页按钮区域 -->
      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
        :current-page="pagination.currentPage" :page-sizes="[2, 5, 10]" :page-size="pagination.pageSize"
        layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" :background="true">
      </el-pagination>
    </el-card>
    <!-- 添加角色按钮点击-添加角色表单区域 -->
    <el-dialog title="添加角色" :visible.sync="addDialogVisible" width="50%" :before-close="addHandleClose"
      :close-on-click-modal="false">
      <el-form :model="addRolesForm" :rules="addFormRules" ref="addFormRef" label-width="120px" class="add_form">
        <el-form-item label="角色名称" prop="roleName">
          <el-input v-model="addRolesForm.roleName"></el-input>
        </el-form-item>
        <el-form-item label="角色描述" prop="roleDesc">
          <el-input v-model="addRolesForm.roleDesc"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addHandleClose">取 消</el-button>
        <el-button type="primary" @click="addRoles">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 操作块-蓝色编辑角色按钮表单区域 -->
    <el-dialog title="编辑角色" :visible.sync="alterDialogVisible" width="50%" :before-close="alterHandleClose"
      :close-on-click-modal="false">
      <el-form :model="alterRolesForm" :rules="addFormRules" ref="addFormRef" label-width="120px" class="add_form">
        <el-form-item label="角色名称" prop="roleName">
          <el-input v-model="alterRolesForm.roleName"></el-input>
        </el-form-item>
        <el-form-item label="角色描述" prop="roleDesc">
          <el-input v-model="alterRolesForm.roleDesc"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="alterHandleClose">取 消</el-button>
        <el-button type="primary" @click="alterRoles">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 操作块-分配权限弹窗添加权限框 -->
    <el-dialog title="分配权限" :visible.sync="setRolesDialogVisible" width="50%" :before-close="setRolesHandleClose"
      :close-on-click-modal=false>
      <!-- 树形控件-所有权限展示区域 props：树形控件对应的属性-->
      <el-tree :data="rightsList" :props="treeProps" show-checkbox node-key="id" :default-checked-keys="defKeys"
        ref="treeRef"></el-tree>
      <span slot="footer" class="dialog-footer">
        <el-button @click="setRolesHandleClose">取 消</el-button>
        <el-button type="primary" @click="allotRights">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        // 所有角色列表
        rolesList1: [],
        // 添加角色表单弹窗显示与隐藏
        addDialogVisible: false,
        // 添加角色的表单信息
        addRolesForm: {
          roleName: '',
          roleDesc: ''
        },
        // 验证-添加角色的表单信息
        addFormRules: {
          roleName: [{
              required: true,
              message: '请输入角色名称',
              trigger: 'blur'
            },
            {
              min: 2,
              max: 10,
              message: '长度在 2 到 10 个字符',
              trigger: 'blur'
            }
          ],
          roleDesc: [{
              required: true,
              message: '请输入角色描述',
              trigger: 'blur'
            },
            {
              min: 3,
              max: 12,
              message: '长度在 3 到 12 个字符',
              trigger: 'blur'
            }
          ]
        },
        // 操作块-需要编辑的角色内容
        alterRolesForm: {
          id: '',
          roleName: '',
          roleDesc: ''
        },
        // 操作块-编辑角色表单弹窗显示与隐藏
        alterDialogVisible: false,
        // 操作块-分配权限弹窗添加权限框显示与隐藏
        setRolesDialogVisible: false,
        // 操作块-分配权限点击后，树形权限弹出框（所有的权限）
        rightsList: [],
        // 树形控件对应的属性
        treeProps: {
          children: 'children', // 嵌套属性
          label: 'authName' // 显示文字
        },
        // 默认选中节点的Id值的权限数组
        defKeys: [],
        // 当前马上要被分配权限的角色id
        roleId: '',
        // 优化：分页所需参数
        pagination: {
          total: 0, // 角色列表总条数
          currentPage: 1, // 当前页数
          pageSize: 5 // 每页显示几条数据
        },
        // 优化：分页控制展示的角色列表
        rolesList2: [],
      }
    },
    created() {
      // 获取所有角色列表
      this.getRolesList()
    },
    methods: {
      // 获取所有角色列表
      async getRolesList() {
        let {
          data: res
        } = await this.$http.get('roles')
        if (res.meta.status === 200) {
          this.rolesList1 = res.data
          this.pagination.total = res.data.length // 分页器总记录数
          this.setRolesList()
        } else {
          return this.$message.error("权限列表获取失败!")
        }
      },


      // 点击添加角色按钮-添加角色对话框出现
      addRolesListBut() {
        this.addDialogVisible = true
      },


      // 添加角色对话框关闭前的回调-清空数据
      addHandleClose() {
        this.addDialogVisible = false
        this.$refs.addFormRef.resetFields()
        this.addRolesForm = {}
      },


      // 添加角色对话框的确定按钮
      addRoles() {
        this.$refs.addFormRef.validate(async (valid, date) => {
          if (!valid) {
            return this.$message.error("输入数据格式不对，添加角色失败！");
          } else {
            let {
              data: res
            } = await this.$http.post('roles', this.addRolesForm)
            if (res.meta.status === 201) {
              this.$message.success("添加角色成功！")
              this.getRolesList()
            } else {
              this.$message.error("添加角色失败！");
            }
            this.addHandleClose()
          }
        })
      },


      // 操作块-红色删除按钮
      removeRolesBut(id) {
        this.$confirm('此操作将永久删除该角色, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning',
          closeOnClickModal: false // 禁止使用遮罩层关闭
        }).then(async () => {
          let {
            data: res
          } = await this.$http.delete(`roles/${id}`)
          if (res.meta.status === 200) {
            this.getRolesList()
            this.$message.success("删除角色成功！")
          } else {
            this.$message.error("删除角色失败！")
          }

        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除角色！'
          });
        });
      },


      // 点击操作块-编辑角色按钮
      alterRolesBut(data) {
        this.alterRolesForm.id = data.id
        this.alterRolesForm.roleName = data.roleName
        this.alterRolesForm.roleDesc = data.roleDesc
        this.alterDialogVisible = true

      },


      // 操作块-编辑角色对话框关闭前的回调-清空数据
      alterHandleClose() {
        this.alterDialogVisible = false
        // 移除编辑表单数据校验结果
        this.$refs.addFormRef.clearValidate()
        this.alterRolesForm = {}
      },


      // 操作块-编辑角色对话框的确定按钮
      async alterRoles() {
        this.$refs.addFormRef.validate(async (valid, date) => {
          if (!valid) {
            return this.$message.error("输入数据格式不对，编辑角色失败！")
          } else {
            let {
              data: res
            } = await this.$http.put(`roles/${this.alterRolesForm.id}`, {
              roleName: this.alterRolesForm.roleName,
              roleDesc: this.alterRolesForm.roleDesc
            })
            if (res.meta.status === 200) {
              this.$message.success("编辑角色成功！")
              this.getRolesList()
            } else {
              this.$message.error("编辑角色失败！")
            }
            this.alterHandleClose()
          }
        })

      },


      // 展开块-橙色三级角色权限关闭按钮
      removeRightById(roles, id2) {
        // roles：角色，id2：需要删除的权限id
        this.$confirm('此操作将永久删除该权限, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning',
          closeOnClickModal: false // 禁止使用遮罩层关闭
        }).then(async () => {
          // roles.id：角色的id，id2：需要删除的权限id
          let {
            data: res
          } = await this.$http.delete(`roles/${roles.id}/rights/${id2}`)
          if (res.meta.status === 200) {
            // 局部赋值更新，不需要重新获取刷新页面关闭展开区域
            roles.children = res.data
            this.$message.success("删除角色权限成功！")
          } else {
            this.$message.error("删除角色权限失败！")
          }
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          });
        });
      },


      // 操作块-分配权限按钮，弹出的对话框
      async setRolesBut(role) {

        // 获取当前要被分配权限的角色id，给allotRights()确定按钮的回调函数请求使用
        this.roleId = role.id
        let {
          data: res
        } = await this.$http.get('rights/tree')
        if (res.meta.status === 200) {
          this.rightsList = res.data
          // 递归获取三级节点的id
          this.getDefKeys(role, this.defKeys)
          this.setRolesDialogVisible = true
        } else {
          this.$message.error("获取树形权限列表失败！")
        }

      },


      // 通过递归形式，获取角色下所有三级权限的id，并保存到 defKeys 数组中
      getDefKeys(node, arr) {
        // 如果当前 node 节点不包含 children 属性，则已经是三级节点
        if (!node.children) {
          // push():方法可向数组的末尾添加一个或多个元素，并返回新的长度
          return arr.push(node.id)
        } else {
          // forEach()：方法用于调用数组的每个元素，并将元素传递给回调函数，对于空数组则不会执行回调函数
          node.children.forEach(item => {
            this.getDefKeys(item, arr)
          })
        }
      },


      // 操作块-分配权限按钮，弹出的对话框的关闭回调
      setRolesHandleClose() {
        this.setRolesDialogVisible = false
        // 关闭后树形数组应该也清空
        this.defKeys = []
      },


      // 操作块-分配权限对话框-确定按钮（为角色分配权限）
      async allotRights() {
        /*--------------------------重点
            ... ：ES6新特性，展开运算符（三个点）
            作用：把数组展开，合并数组、合并对象、函数参数传递时都可以使用
            详细：https://zhuanlan.zhihu.com/p/161961356
        */

        // 将获取到的所有id变成新数组
        let keys = [
          ...this.$refs.treeRef.getCheckedKeys(),
          ...this.$refs.treeRef.getHalfCheckedKeys()
        ]

        // 将数组元素转换为字符串，并以()内的内容分割（发起请求要用）
        const idStr = keys.join(",")


        /*-----------------------重点-----------------------
          接口文档若写通过请求体发送
          则是在 url, 后面加{}，里面放对应请求参数
          如：this.$http.post(`...`, {请求参数: 对应值})
        */
        let {
          data: res
        } = await this.$http.post(`roles/${this.roleId}/rights`, {
          rids: idStr
        })
        if (res.meta.status === 200) {
          this.getRolesList()
          this.$message.success("该角色的权限更新成功！")
        } else {
          this.$message.error("该角色的权限更新失败！")
        }
        this.setRolesHandleClose()
      },


      // 优化：分页-每页几条数据
      handleSizeChange(val) {
        this.pagination.pageSize = val
        this.setRolesList()

      },


      // 优化：分页-当前页数
      handleCurrentChange(val) {
        this.pagination.currentPage = val
        this.setRolesList()

      },


      // 优化：请求直接返回全部数据的分页展示方法
      setRolesList() {
        /*------------------------------------- 重点 ---------------------------------
          数据在请求时就全部获取到了，所以不再是按参数获取对应分页数据了
          需要自己截取分页对应的那段数组内容，使用：slice()方法截取数组
          返回一个索引和另一个索引之间的数据，不改变原数组
          slice(开始索引，结束索引)，必填参数两个
          而-开始索引为：(当前页数-1)*每页显示条数 ； 结束索引为：当前页数 * 每页显示条数
        */
        let index = (this.pagination.currentPage - 1) * this.pagination.pageSize
        let index2 = this.pagination.currentPage * this.pagination.pageSize

        this.rolesList2 = this.rolesList1.slice(index,index2)
      }
    }
  }

</script>

<style lang="less" scoped>
  /* @import url('../../assets/css/Roles.less'); */

</style>
